import { defineAsyncComponent, reactive, watch, markRaw, inject } from 'vue'

import type { 
  IMenuList,
  IMenus,
  IMenu,
  IRouter,
  ICurrentRoute,
  IProps
} from '../types/90-router'

const flag = Symbol('nf-router-menu___')

/**
 * 把树状态的菜单，变成单层的菜单，便于用 key 查找菜单
 */
const treeToKey = (menus: Array<IMenu>, list: IMenuList, paths = '') => {
  menus.forEach((_menu: IMenu) => {
    let _comp = (_menu.component) ?? ''
    // 有组件，判断是否函数
    if (typeof _comp === 'function') {
      _comp = defineAsyncComponent(_comp)
    }

    list[_menu.menuId] = {
      menuId: _menu.menuId,
      title: _menu.title,
      path: _menu.path,
      paths: paths + '/' + _menu.path,
      component: _comp
    }
    if (Array.isArray(_menu.childrens)) {
      treeToKey(_menu.childrens, list, list[_menu.menuId].paths)
    }
  })
  return list
}

/**
 * 一个简单的路由
 * @param { string } baseUrl 基础路径
 * @param { components } home 基础路径
 * @param { array } menus 路由设置，数组，多级
 * * [{
 * * *  menuId: '菜单ID'
 * * *  naviId: '0', // 导航ID，可以不设置
 * * *  title: '标题',
 * * *  path: '路径',
 * * *  icon: Edit, // 图标组件
 * * *  component: () => import('./views/xxx.vue') // 要加载的组件，可以不设置
 * * *  childrens: [ // 子菜单，可以多级
 * * * *  {
 * * * * *  menuId: '菜单ID' 
 * * * * *  title: '标题',
 * * * * *  path: '路径',
 * * * * *  icon: Edit, // 图标组件
 * * * * *  component: () => import('./views/xxx.vue') // 要加载的组件
 * * * * }
 * * * ]
 * * },
 * * 其他菜单
 * * ]
 */
class Router implements IRouter {
  // 基础路径，应对网站的二级目录
  baseUrl: string
  // 初始的浏览器标题
  baseTitle: string
  // 是否刷新进入
  isRefresh: boolean
  // 默认的首页
  home: any
  // 菜单集合
  menus: Array<IMenu>
  menuList: IMenuList
  tabs: any
  currentRoute: ICurrentRoute

  constructor (info: IMenus) {
    // 设置当前选择的路由
    this.currentRoute = reactive({
      key: 'home', // 默认的首页
      paths: [] // 记录打开的多级菜单的信息 
    })
    this.baseUrl = info.baseUrl // 基础路径，应对网站的二级目录
    this.home = info.home // 默认的首页 markRaw
    // this.menus = reactive(info.menus) // 菜单集合，数组形式，支持多级，可以设置导航ID
    this.menus = markRaw(info.menus) // 菜单集合，数组形式，支持多级，可以设置导航ID
  
    this.baseTitle = document.title // 初始的标题
    this.isRefresh = false // 是否刷新进入
    this.menuList = {} // 变成单层的树，便于用key查找。
    this.tabs = reactive(new Set([])) // 点击过且没有关闭的二级菜单，做成动态tab标签
   
    this.setup()
  }

  /**
   * 初始化设置
   */
  setup = () => {
    // 监听当前路由，设置 tabs 和标题、url
    watch(() => this.currentRoute.key, (key) => {
      if (key !== 'home' ) this.tabs.add(key)
      if (this.isRefresh) {
        this.isRefresh = false
        return
      }
      // 设置标题和URL
      let _title = this.baseTitle
      let _url = this.baseUrl
      this.currentRoute.paths.forEach(key => {
        const _menu = this.menuList[key]
        _title = _menu.title + '-' + _title
        _url += '/' + _menu.path
      })
      document.title = _title
      // 设置url地址
      window.history.pushState(null, null, _url)
    }) // , {immediate: true}

    // 把路由里的组件转换一下
    this.menuList = treeToKey(this.menus, {})
  }

  /**
   * 动态添加新路由
   */
  addRoute<T extends IProps>(newMenus: Array<IMenu>, props: T): void {
    // 默认是根菜单
    let _parent = this.menus

    const path = props.path ?? []
    const index = props.index ?? null

    if (path.length > 0) {
      path.forEach((id: string | number) => {
        const tmp = _parent.find(m => m.menuId === id)
        if (!tmp) {
          // 没有找到 父级菜单
          console.error('没有找到 父级菜单：', id, this.menus)
          return
        }
        if (!tmp.childrens) {
          // 没有子菜单
          tmp.childrens = []
        }
        _parent = tmp.childrens
      })
    }

    // 判断是否有相同 menuId
    newMenus.forEach((_menu: IMenu) => {
      if (_parent.findIndex((item) => item.menuId === _menu.menuId) === -1) {
        // 没有，添加新路由
        // 判断位置
        if (index === null) {
          // 填在最后
          _parent.push(_menu)
        } else {
          // 指定位置
          _parent.splice(index, 0, _menu)
        }
      }
    })
    const tmp = treeToKey(newMenus, {})
    // 把路由里的组件转换一下
    Object.assign(this.menuList, tmp)
  }

  /**
   * 删除路由
   * @param { array } path 菜单的路径，[] 表示根菜单
   * @param { string | number } id 要删除的菜单ID
   */
  removeRoute (path = [], id = '') {
    // 默认是根菜单
    let _parent = this.menus

    let hasParent = true
    
    if (path.length > 0) {
      path.forEach((id, i) => {
        const tmp = _parent.find(m => m.menuId === id)
        if (!tmp) {
          // 没有找到 父级菜单
          console.error(`没有找到 父级菜单，位置：${i}，ID：${id}，菜单：`, this.menus)
          hasParent = false
        }
        _parent = tmp.childrens
        if (!_parent) {
          // 没有子菜单
          console.error(`没有子菜单，path：`, path, this.menus)
          hasParent = false
        }
      })
    }

    // 删除
    if (hasParent) {
      const index = _parent.findIndex(m => m.menuId === id)
      if (index > -1) {
        _parent.splice(index, 1)
      }
    }
  }

  /**
   * 刷新时依据url加载组件
   */
  refresh = () => {
    const path = window.location.pathname
    if (path === '/' || path === this.baseUrl) {
      // 首页
    } else {
      this.isRefresh = true
      const tmp = path.replace(this.baseUrl, '')
      // 根据URL加载组件
      Object.keys(this.menuList).forEach(key => {
        const _menu = this.menuList[key]
        if (_menu.paths === tmp) {
          this.currentRoute.key = key
          document.title = _menu.title + '-' + this.baseTitle
        }
      })
    }
  }

  /**
   * 加载路由指定的组件
   * @returns 
   */
  getComponent = () => {
    if (this.currentRoute.key === '' || this.currentRoute.key === 'home') {
      return this.home
    } else {
      return this.menuList[this.currentRoute.key].component
    }
  }

  /**
   * 删除tab
   * @param { string } key 
   * @returns 
   */
  removeTab = (key) => {
    // 转换为数组，便于操作
    const arr = Array.from(this.tabs)
    
    if (arr.length === 1) {
      // 只有一个tab，删除后激活桌面
      this.tabs.delete(key)
      this.currentRoute.key = 'home'
      return
    }

    // 判断是否当前tab，如果是当前tab，激活左面或者右面的tab
    if (this.currentRoute.key === key) {
      // 查找当前tab的数组序号
      const index = arr.indexOf(key)
      // 判断当前tab的位置
      if (index === 0) {
        // 第一位，激活后面的
        this.currentRoute.key = arr[1]
      } else {
        // 激活前面的
        this.currentRoute.key = arr[index - 1]
      }
    }
    // 删除
    this.tabs.delete(key)
  }

  /**
   * 安装插件
   * @param {*} app 
   */
  install = (app) => {
    // 便于模板获取
    app.config.globalProperties.$router = this
    // 便于代码获取
    app.provide(flag, this)
  }
}

/**
 * 创建简易路由
 */
const createRouter = (info: IMenus) => {
  // 创建路由，
  const router = new Router(info)
  // 判断url，是否需要加载组件
  setTimeout(() => {
    router.refresh()
  }, 300)
  // 使用vue的插件，设置全局路由
  return router
}

/**
 * 获取路由
 * @returns 
 */
function useRouter(): Router {
  return inject(flag)
}

export {
  createRouter,
  useRouter
}